package com.emeraldparser.util;

import com.emeraldparser.util.Graph.Edge;
import com.google.common.base.Objects;

public final class UndirectedGraphEdge<V, E> extends AbstractGraphEdge<V, E> {

	private final transient int hashCode;

	private UndirectedGraphEdge(V source, V destination, E label) {
		super(source, destination, label);
		
		int sourceHashCode = (source == null) ? 0 : source.hashCode();
		int destinationHashCode = (destination == null) ? 0 : destination.hashCode();
		
		V min, max;
		if (sourceHashCode < destinationHashCode) {
			min = source;
			max = destination;
		} else {
			min = destination;
			max = source;
		}
		
		this.hashCode = Objects.hashCode(min, max, label, false);
	}

	public static <V, E> UndirectedGraphEdge<V, E> create(V source, V destination, E label) {
		return new UndirectedGraphEdge<V, E>(source, destination, label);
	}

	@Override
	public boolean isDirected() {
		return false;
	}

	@Override
	public String toString() {
		return getSource() + "-" + getLabel() + "-" + getDestination();
	}

	@Override
	public int hashCode() {
		return hashCode;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		
		if (obj == null) {
			return false;
		}
		
		if (!(obj instanceof UndirectedGraphEdge<?, ?>)) {
			return false;
		}
		
		Graph.Edge<?, ?> other = (Graph.Edge<?, ?>) obj;
		
		if (other.isDirected()) {
			return false;
		}
		
		if (!Objects.equal(getLabel(), other.getLabel())) {
			return false;
		}
		
		return ( Objects.equal(getSource(), other.getSource()) && Objects.equal(getDestination(), other.getDestination()) )
		    || ( Objects.equal(getSource(), other.getDestination()) && Objects.equal(getDestination(), other.getSource()) );
	}

	@Override
	public boolean comesFrom(V vertex) {
		return Objects.equal(vertex, getSource())
		    || Objects.equal(vertex, getDestination());
	}

	@Override
	public boolean goesTo(V vertex) {
		return Objects.equal(vertex, getSource())
		    || Objects.equal(vertex, getDestination());
	}

	@Override
	public boolean isBetween(V source, V destination) {
		return ( Objects.equal(source, getSource()) && Objects.equal(destination, getDestination()) )
		    || ( Objects.equal(source, getDestination()) && Objects.equal(destination, getSource()) );
	}

	@Override
	public Edge<V, E> invert() {
		return new UndirectedGraphEdge<V, E>(getDestination(), getSource(), getLabel());
	}

}
